/****************************************************
	文件：ResItem.cs
	作者：HuskyT
	邮箱：1005240602@qq.com
	日期：2020/9/27 11:27:30
	功能：资源数据项
*****************************************************/

#define IN_EDIT_MODE

using System;
using UnityEngine;
using HTUtility;
using System.Collections.Generic;
using System.Diagnostics;

namespace HTFramework.ResKit
{
    public class ResItem : RefCounter
    {
        private ResRecorderType mRecorderType;
        private ResLoadState mLoadState;
        private bool mClearMemory;
        private Action<ResItem> OnLoadedEvent;
        /// <summary>
        /// 资源路径
        /// </summary>
        public string Path;
        /// <summary>
        /// 资源路径对应的Crc
        /// </summary>
        public uint Crc;
        /// <summary>
        /// 缓存类型
        /// </summary>
        public ResRecorderType RecorderType
        {
            get => mRecorderType;
            set
            {
                if (mRecorderType != value)
                {
                    HTLogger.Debug("{0} 改变 RecorderType , \n{1} => {2}", AssetName, mRecorderType.ToString(), value.ToString());
                    ResRecorder.Instance.RecordResItem(this, value); //记录（移出当前缓存+添加到新缓存）
                    mRecorderType = value;
                }
            }
        }
        /// <summary>
        /// 加载状态
        /// </summary>
        public ResLoadState LoadState
        {
            get => mLoadState;
            set
            {
                mLoadState = value;
                if (value == ResLoadState.LOADED && RecorderType != ResRecorderType.IDLE) //添加条件：RecorderType != ResRecorderType.IDLE  是防止在预加载完成回调中执行 卸载 导致的死循环
                {
                    HTLogger.Warning("{0} 加载完成，开始执行完成回调", AssetName);
                    OnLoadedEvent?.Invoke(this);
                    HTLogger.Warning("{0} 完成回调执行完毕，置空完成回调", AssetName);
                    OnLoadedEvent = null;
                }
            }
        }
        /// <summary>
        /// 是否清除内存
        /// </summary>
        public bool ClearMemory
        {
            get => mClearMemory;
        }
        /// <summary>
        /// 资源名
        /// </summary>
        public string AssetName;
        /// <summary>
        /// 资源所在AssetBundle名
        /// </summary>
        public string ABName;
        /// <summary>
        /// AssetBundle依赖项名
        /// </summary>
        public List<string> ABDependencyList;
        /// <summary>
        /// 资源对象
        /// </summary>
        public UnityEngine.Object Obj { get; private set; }
        /// <summary>
        /// 自身资源所在AssetBundle
        /// </summary>
        public AssetBundle OwnAB { get; private set; }


        public ResItem() : base()
        {
            RecorderType = ResRecorderType.DEFAULT;
            LoadState = ResLoadState.UNLOADED;
            AssetName = string.Empty;
            ABName = string.Empty;
            ABDependencyList = new List<string>();
            Obj = null;
            OwnAB = null;
            OnLoadedEvent = null;
            mClearMemory = true;
        }
        /// <summary>
        /// RefCount ≤ 0时自动调用（外部无需手动调用）
        /// </summary>
        public override void OnDispose()
        {
#if IN_EDIT_MODE && UNITY_EDITOR
            ReleaseInEditMode();
#else
            HTLogger.Debug("{0} 执行打包模式下的资源卸载", AssetName);
            if (ClearMemory)
            {
                RecorderType = ResRecorderType.UNLOADED;
                LoadState = ResLoadState.UNLOADED;
                Obj = null;
                OwnAB = null;
                OnLoadedEvent = null;
            }
            else
            {
                if (Obj != null)
                {
                    RecorderType = ResRecorderType.IDLE;
                    LoadState = ResLoadState.LOADED;
                    OnLoadedEvent = null;
                }
            }
            mClearMemory = true;
#endif
        }
        /// <summary>
        /// 重置ResItem，便于对象池回收
        /// </summary>
        public void Reset()
        {
            Path = string.Empty;
            Crc = 0;
            RecorderType = ResRecorderType.DEFAULT;
            LoadState = ResLoadState.UNLOADED;
            AssetName = string.Empty;
            ABName = string.Empty;
            ABDependencyList.Clear();
            Obj = null;
            OwnAB = null;
            OnLoadedEvent = null;
            mClearMemory = true;
        }

        public void SubscribeLoadedEvent(Action<ResItem> onLoaded)
        {
            HTLogger.Debug("{0} 注册完成回调", AssetName);
            OnLoadedEvent += onLoaded;
        }

        /// <summary>
        /// 同步加载
        /// </summary>
        public void DoLoadSync()
        {
            LoadState = ResLoadState.LOADING;
#if IN_EDIT_MODE && UNITY_EDITOR
            LoadInEditMode();
#else
            ABItem abItem = null;

            HTLogger.Debug("（同步加载）{0} 开始同步加载", AssetName);
            //1.加载依赖项
            foreach (string abName in ABDependencyList)
            {
                HTLogger.Debug("（同步加载）{0} 从缓存中获取依赖项：{1}", AssetName, abName);
                abItem = ResRecorder.Instance.GetABItem(abName);
                if (abItem == null) continue;
                if (abItem.AssetBundle == null)
                {
                    HTLogger.Debug("（同步加载）{0} 开始同步加载依赖项：{1}", AssetName, abName);
                    abItem.DoLoadSync();
                }
                else
                {
                    abItem.Retain();
                    HTLogger.Debug("（同步加载）{0} 缓存中获取的依赖项已经加载完成，引用计数+1，当前引用计数：{1}", abItem.ABName, abItem.RefCount);
                    abItem.RecorderType = ResRecorderType.USING;
                }
            }
            HTLogger.Debug("（同步加载）{0} 从缓存中获取自身AB包：{1}", AssetName, ABName);
            abItem = ResRecorder.Instance.GetABItem(ABName);
            if (abItem == null) return;
            //2.加载自身AB包
            if (OwnAB == null)
            {
                if (abItem.AssetBundle == null)
                {
                    HTLogger.Debug("（同步加载）{0} 开始同步加载自身AB包：{1}", AssetName, ABName);
                    abItem.DoLoadSync();
                    HTLogger.Debug("（同步加载）{0} 同步加载自身AB包（{1}）完毕，添加AB资源引用到ResItem", AssetName, ABName);
                }
                else
                {
                    abItem.Retain();
                    HTLogger.Debug("（同步加载）{0} 缓存中获取的自身AB包已经加载完成，引用计数+1，当前引用计数：{1}", abItem.ABName, abItem.RefCount);
                    abItem.RecorderType = ResRecorderType.USING;
                }
                OwnAB = abItem.AssetBundle;
            }
            else
            {
                abItem.Retain();
                HTLogger.Debug("（同步加载）{0} 已经加载过了，引用计数+1，当前引用计数：{1}", abItem.ABName, abItem.RefCount);
                abItem.RecorderType = ResRecorderType.USING;
            }
            HTLogger.Debug("（同步加载）{0} 从自身AB包（{1}）中同步加载资源", AssetName, ABName);
            //3.从AB包中加载资源
            if (Obj == null)
            {
                Obj = OwnAB.LoadAsset(AssetName);
            }
            Retain();
            HTLogger.Debug("（同步加载）{0} 加载完成，引用计数+1，当前引用计数：{1}", AssetName, mRefCount.Value);
            RecorderType = ResRecorderType.USING;
            LoadState = ResLoadState.LOADED;
#endif
        }

        /// <summary>
        /// 异步加载
        /// </summary>
        public void DoLoadAsync()
        {
            LoadState = ResLoadState.LOADING;
            HTLogger.Debug("（异步加载）{0} 开始异步加载", AssetName);
#if IN_EDIT_MODE && UNITY_EDITOR
            LoadInEditMode();
#else
            LoadDependencyABAsync();
#endif
        }

        /// <summary>
        /// 卸载资源
        /// </summary>
        public void DoRelease(bool clearMemory = true)
        {
            mClearMemory = clearMemory;
#if IN_EDIT_MODE && UNITY_EDITOR

#else
            ABItem abItem;
            //1.依赖项卸载：
            foreach (string abName in ABDependencyList)
            {
                abItem = ResRecorder.Instance.GetABItem(abName);
                if (abItem == null) continue;
                abItem.DoRelease(clearMemory);
            }
            //2.自身AB包卸载：
            abItem = ResRecorder.Instance.GetABItem(ABName);
            if (abItem == null) return;
            abItem.DoRelease(clearMemory);
#endif
            //3.卸载自身
            Release();
            HTLogger.Debug("{0} 卸载，引用计数减少，当前引用计数：{1}", AssetName, RefCount);
        }

        /// <summary>
        /// 异步加载AB包的依赖项
        /// </summary>
        private void LoadDependencyABAsync()
        {
            if (ABDependencyList.Count == 0)
            {
                HTLogger.Debug("（异步加载）{0} 无依赖项", AssetName);
                LoadOwnABAsync(); //异步加载自身AB包
            }

            //1.加载AB包依赖项
            ABItem abItem = null;
            //待加载AB包依赖项总数
            int count = 0;
            foreach (string abName in ABDependencyList)
            {
                HTLogger.Debug("（异步加载）{0} 从缓存中获取依赖项：{1}", AssetName, abName);
                abItem = ResRecorder.Instance.GetABItem(abName);
                if (abItem == null) continue;
                switch (abItem.LoadState)
                {
                    case ResLoadState.UNLOADED:
                        HTLogger.Debug("（异步加载）{0} 绑定尚未加载的依赖项 {1} 的加载完成回调", AssetName, abName);
                        //绑定完成回调
                        abItem.SubscribeLoadedEvent((ab) =>
                        {
                            count++;
                            HTLogger.Debug("（异步加载）{0} 的依赖项 {1} 加载完成，{2}/{3}", AssetName, abName, count, ABDependencyList.Count);
                            if (count >= ABDependencyList.Count)
                            {
                                LoadOwnABAsync(); //异步加载自身AB包
                            }
                        });
                        HTLogger.Debug("（异步加载）{0} 开始加载依赖项 {1} ", AssetName, abName);
                        //加载
                        abItem.DoLoadAsync();
                        break;
                    case ResLoadState.LOADING:
                        HTLogger.Debug("（异步加载）{0} 绑定正在加载的依赖项 {1} 的加载完成回调", AssetName, abName);
                        //绑定完成回调
                        abItem.SubscribeLoadedEvent((ab) =>
                        {
                            count++;
                            ab.Retain();//TODO
                            HTLogger.Debug("（异步加载）{0} 加载完成，引用计数+1，当前引用计数：{1}", ab.ABName, ab.RefCount);
                            ab.RecorderType = ResRecorderType.USING;
                            HTLogger.Debug("（异步加载）{0} 的依赖项 {1} 加载完成，{2}/{3}", AssetName, abName, count, ABDependencyList.Count);
                            if (count >= ABDependencyList.Count)
                            {
                                LoadOwnABAsync(); //异步加载自身AB包
                            }
                        });
                        break;
                    case ResLoadState.LOADED:
                        if (abItem.AssetBundle == null)
                        {
                            HTLogger.Error("LoadABAsync error ! ABItem 的加载状态出错，ABName：{0}", abName);
                        }
                        else //跳过加载
                        {
                        }
                        count++;
                        abItem.Retain();//TODO
                        HTLogger.Debug("（异步加载）{0} 已经加载过了，引用计数+1，当前引用计数：{1}", abItem.ABName, abItem.RefCount);
                        abItem.RecorderType = ResRecorderType.USING;
                        HTLogger.Debug("（异步加载）{0} 的依赖项 {1} 已加载，无需重新加载，{2}/{3}", AssetName, abName, count, ABDependencyList.Count);
                        if (count >= ABDependencyList.Count)
                        {
                            LoadOwnABAsync(); //异步加载自身AB包
                        }
                        break;
                }
            }
        }

        /// <summary>
        /// 异步加载自身AB包
        /// </summary>
        /// <param name="onAllLoaded"></param>
        private void LoadOwnABAsync()
        {
            HTLogger.Debug("（异步加载）{0} 开始加载自身AB包：{1}", AssetName, ABName);
            HTLogger.Debug("（异步加载）{0} 从缓存中获取自身AB包：{1}", AssetName, ABName);
            ABItem abItem = ResRecorder.Instance.GetABItem(ABName);
            if (OwnAB != null)
            {
                abItem.Retain();//TODO
                HTLogger.Debug("（异步加载）{0} 自身AB包已经加载过了，引用计数+1，当前引用计数：{1}", abItem.ABName, abItem.RefCount);
                abItem.RecorderType = ResRecorderType.USING;
                HTLogger.Debug("（异步加载）{0} 自身AB包已加载，无需重新加载：{1}", AssetName, ABName);
                LoadAssetAsync();
                return;
            }
            if (abItem == null) return;
            if (abItem.AssetBundle == null)
            {
                switch (abItem.LoadState)
                {
                    case ResLoadState.UNLOADED:
                        HTLogger.Debug("（异步加载）{0} 绑定尚未加载的自身AB包 {1} 的加载完成回调", AssetName, ABName);
                        //绑定完成回调
                        abItem.SubscribeLoadedEvent((ab) =>
                        {
                            OwnAB = ab.AssetBundle;
                            LoadAssetAsync();
                        });
                        HTLogger.Debug("（异步加载）{0} 开始加载自身AB包 {1} ", AssetName, ABName);
                        //加载
                        abItem.DoLoadAsync();
                        break;
                    case ResLoadState.LOADING:
                        HTLogger.Debug("（异步加载）{0} 绑定正在加载的自身AB包 {1} 的加载完成回调", AssetName, ABName);
                        //绑定完成回调
                        abItem.SubscribeLoadedEvent((ab) =>
                        {
                            ab.Retain();//TODO
                            HTLogger.Debug("（异步加载）{0} 加载完成，引用计数+1，当前引用计数：{1}", ab.ABName, ab.RefCount);
                            ab.RecorderType = ResRecorderType.USING;
                            OwnAB = ab.AssetBundle;
                            LoadAssetAsync();
                        });
                        break;
                    case ResLoadState.LOADED:
                        HTLogger.Error("LoadOwnABAsync error ! ABItem 的加载状态出错，ABName：{0}", ABName);
                        break;
                }
            }
            else
            {
                HTLogger.Debug("（异步加载）{0} 的自身AB包 {1} 从缓存中获取，发现其已经加载完成", AssetName, ABName);
                OwnAB = abItem.AssetBundle;
                abItem.Retain();//TODO
                HTLogger.Debug("（异步加载）{0} 已经加载过了，引用计数+1，当前引用计数：{1}", abItem.ABName, abItem.RefCount);
                abItem.RecorderType = ResRecorderType.USING;
                LoadAssetAsync();
            }
        }

        /// <summary>
        /// 加载 OwnAB 完成时的回调
        /// </summary>
        private void LoadOwnABFinishedCallBack(ABItem abItem)
        {
            OwnAB = abItem.AssetBundle;
            LoadAssetAsync();
        }

        /// <summary>
        /// 异步加载 OwnAB 中的 Asset 资源
        /// </summary>
        private void LoadAssetAsync()
        {
            HTLogger.Debug("（异步加载）{0} 从自身AB包 {1} 中开始异步加载资源", AssetName, ABName);
            if (Obj != null) //已经加载过Asset
            {
                Retain();
                HTLogger.Debug("（异步加载）{0} 已经加载过了，引用计数+1，当前引用计数：{1}", AssetName, mRefCount.Value);
                RecorderType = ResRecorderType.USING;
                LoadState = ResLoadState.LOADED;
                return;
            }
            if (OwnAB != null) //加载Asset
            {
                AssetBundleRequest request = OwnAB.LoadAssetAsync(AssetName);
                request.completed += operation =>
                {
                    Obj = request.asset;

                    Retain();
                    HTLogger.Debug("（异步加载）{0} 加载完成，引用计数+1，当前引用计数：{1}", AssetName, mRefCount.Value);
                    RecorderType = ResRecorderType.USING;
                    LoadState = ResLoadState.LOADED;
                };
            }
            else
            {
                HTLogger.Error("LoadAssetAsync error ! OwnAB is null , ABName : {0} , AssetName : {1}", ABName, AssetName);
            }
        }

#if UNITY_EDITOR
        private void LoadInEditMode()
        {
            if (System.Object.ReferenceEquals(Obj, null))
            {
                Obj = UnityEditor.AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(Path);
                Retain();
                HTLogger.Debug("编辑器模式下加载 {0}，引用计数+1，当前引用计数：{1}", AssetName, mRefCount.Value);
            }
            else
            {
                Retain();
                HTLogger.Debug("编辑器模式下： {0} 已经加载过了，引用计数+1，当前引用计数：{1}", AssetName, mRefCount.Value);
            }
            RecorderType = ResRecorderType.USING;
            LoadState = ResLoadState.LOADED;
        }
        private void ReleaseInEditMode()
        {
            HTLogger.Debug("{0} 执行编辑器模式下的资源卸载", AssetName);
            if (ClearMemory)
            {
                RecorderType = ResRecorderType.UNLOADED;
                LoadState = ResLoadState.UNLOADED;
                Obj = null;
                OnLoadedEvent = null;
                Resources.UnloadUnusedAssets();
            }
            else
            {
                if (Obj != null)
                {
                    RecorderType = ResRecorderType.IDLE;
                    LoadState = ResLoadState.LOADED;
                    OnLoadedEvent = null;
                }
            }
            mClearMemory = true;
        }
#endif
    }
}
